Interval list intersections

Time: O(M+N); Space: O(1); medium

Given two lists of closed intervals, each list of intervals is pairwise disjoint and in sorted order.

Return the intersection of these two interval lists.

(Formally, a closed interval [a, b] (with a <= b) denotes the set of real numbers x with a <= x <= b. The intersection of two closed intervals is a set of real numbers that is either empty, or can be represented as a closed interval. For example, the intersection of [1, 3] and [2, 4] is [2, 3].)

Example 1:

Input: A = [[0,2],[5,10],[13,23],[24,25]], B = [[1,5],[8,12],[15,24],[25,26]]

Output: [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]

Constraints:

  • 0 <= len(A) < 1000

  • 0 <= len(B) < 1000

  • 0 <= A[i].start, A[i].end, B[i].start, B[i].end < 10^9

[12]:
class Interval(object):
    def __init__(self, s=0, e=0):
        self.start = s
        self.end = e

1. Merge Intervals

Intuition

In an interval [a, b], call b the “endpoint”.

Among the given intervals, consider the interval A[0] with the smallest endpoint. (Without loss of generality, this interval occurs in array A.)

Then, among the intervals in array B, A[0] can only intersect one such interval in array B. (If two intervals in B intersect A[0], then they both share the endpoint of A[0] – but intervals in B are disjoint, which is a contradiction.)

Algorithm

If A[0] has the smallest endpoint, it can only intersect B[0]. After, we can discard A[0] since it cannot intersect anything else.

Similarly, if B[0] has the smallest endpoint, it can only intersect A[0], and we can discard B[0] after since it cannot intersect anything else.

We use two pointers, i and j, to virtually manage “discarding” A[0] or B[0] repeatedly.

[13]:
class Solution1(object):
    """
    Time: O(M+N), where M, N are the lengths of A and B respectively.
    Space: O(M+N), the maximum size of the answer.
    """
    def intervalIntersection(self, A, B):
        """
        :type A: List[Interval]
        :type B: List[Interval]
        :rtype: List[Interval]
        """
        result = []
        i, j = 0, 0

        while i < len(A) and j < len(B):

            # Let's check if A[i] intersects B[j].
            # left - the startpoint of the intersection
            # right - the endpoint of the intersection
            left = max(A[i].start, B[j].start)
            right = min(A[i].end, B[j].end)

             # Remove the interval with the smallest endpoint
            if left <= right:
                result.append(Interval(left, right))
            if A[i].end < B[j].end:
                i += 1
            else:
                j += 1

        return result
[14]:
s = Solution1()

A = [Interval(0,2), Interval(5,10), Interval(13,23), Interval(24,25)]
B = [Interval(1,5), Interval(8,12), Interval(15,24), Interval(25,26)]
res = s.intervalIntersection(A, B)
exp = [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]
for i in range(len(res)):
    assert res[i].start == exp[i][0]
    assert res[i].end == exp[i][1]